#include "stdafx.h"
#include "event/event.hpp"


#if defined(JH_OS_LINUX)
#include <sys/time.h>
#endif

namespace JHCPP
{
	namespace event
	{	
		CEvent::CEvent(bool autoReset /* = true */)
		{
		#if defined(JH_OS_WINDOWS)
			m_event = ::CreateEvent(NULL, autoReset ? FALSE : TRUE, FALSE, NULL);
			if (!m_event)
				throw SystemException("cannot create event");
		#elif defined(JH_OS_LINUX)
			m_auto = autoReset;
			m_state = false;
			if (pthread_mutex_init(&_mutex, NULL))
				throw SystemException("cannot create event (mutex)");
			if (pthread_cond_init(&_cond, NULL))
				throw SystemException("cannot create event (condition)");
		#endif
		}

		CEvent::~CEvent()
		{
		#if defined(JH_OS_WINDOWS)
			::CloseHandle(m_event);
		#elif defined(JH_OS_LINUX)
			pthread_cond_destroy(&_cond);
			pthread_mutex_destroy(&_mutex);
		#endif
		}

		void CEvent::set()
		{
		#if defined(JH_OS_WINDOWS)
			if (!::SetEvent(m_event))
			{
				throw SystemException("cannot signal event");
			}	
		#elif defined(JH_OS_LINUX)
			if (pthread_mutex_lock(&m_mutex))	
				throw SystemException("cannot signal event (lock)");
			m_state = true;
			if (pthread_cond_broadcast(&m_cond))
			{
				pthread_mutex_unlock(&m_mutex);
				throw SystemException("cannot signal event");
			}
			pthread_mutex_unlock(&m_mutex);
		#endif
		}
		
		void CEvent::wait()
		{
		#if defined(JH_OS_WINDOWS)
			switch (::WaitForSingleObject(m_event, INFINITE))
			{
			case WAIT_TIMEOUT:
				throw TimeoutException();
			case WAIT_OBJECT_0:
				return;
			default:
				throw SystemException("wait for event failed");
			}
		#elif defined(JH_OS_LINUX)
			if (pthread_mutex_lock(&m_mutex))
				throw SystemException("wait for event failed (lock)"); 
			while (!m_state) 
			{
				if (pthread_cond_wait(&m_cond, &m_mutex))
				{
					pthread_mutex_unlock(&m_mutex);
					throw SystemException("wait for event failed");
				}
			}
			if (m_auto)
				m_state = false;
			pthread_mutex_unlock(&m_mutex);
		#endif
		}

		bool CEvent::wait(long milliseconds)
		{
		#if defined(JH_OS_WINDOWS)
			switch (::WaitForSingleObject(m_event, milliseconds + 1))
			{
			case WAIT_TIMEOUT:
				throw TimeoutException();
			case WAIT_OBJECT_0:
				return true;
			default:
				throw SystemException("wait for event failed");		
			}
		#elif defined(JH_OS_LINUX)
			int rc = 0;
			struct timespec abstime;
			struct timeval tv;
			gettimeofday(&tv, NULL);
			abstime.tv_sec  = tv.tv_sec + milliseconds / 1000;
			abstime.tv_nsec = tv.tv_usec*1000 + (milliseconds % 1000)*1000000;
			if (abstime.tv_nsec >= 1000000000)
			{
				abstime.tv_nsec -= 1000000000;
				abstime.tv_sec++;
			}
			if (pthread_mutex_lock(&m_mutex) != 0)
				throw SystemException("wait for event failed (lock)"); 
			while (!m_state) 
			{
				if ((rc = pthread_cond_timedwait(&m_cond, &m_mutex, &abstime)))
				{
					if (rc == ETIMEDOUT) break;
					pthread_mutex_unlock(&m_mutex);
					throw SystemException("cannot wait for event");
				}
			}
			if (rc == 0 && m_auto) m_state = false;
			pthread_mutex_unlock(&m_mutex);
			return rc == 0;
		#endif
		}

		bool CEvent::tryWait(long milliseconds)
		{
			return wait(milliseconds);
		}

		void CEvent::reset()
		{
		#if defined(JH_OS_WINDOWS)
			if (!::ResetEvent(m_event))
			{
				throw SystemException("cannot reset event");
			}
		#elif defined(JH_OS_LINUX)
			if (pthread_mutex_lock(&m_mutex))	
				throw SystemException("cannot reset event");
			m_state = false;
			pthread_mutex_unlock(&m_mutex);
		#endif
		}

	}//end of namespace event
}//end of namespace JHCPP
